<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>医生端 · 脑梗康复管理</title>
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <style>
    :root {
      --bg: #f6f7fb; --card: #ffffff; --text: #111; --muted: #666;
      --primary: #316CFF; --success: #22A06B; --warn: #F5A524; --danger: #E5484D;
      --border: #e5e7eb; --shadow: 0 4px 12px rgba(0,0,0,.06);
    }
    * { box-sizing: border-box; } body { margin: 0; font-family: Arial, Helvetica, sans-serif; background: var(--bg); color: var(--text); }
    .container { max-width: 1100px; margin: 0 auto; padding: 20px; }
    .card { background: var(--card); border: 1px solid var(--border); border-radius: 16px; padding: 16px; box-shadow: var(--shadow); }
    .row { display: flex; gap: 12px; align-items: center; } .space { height: 12px; }
    h1 { font-size: 22px; margin: 0 0 8px; } h2 { font-size: 18px; margin: 12px 0; }
    label { font-size: 14px; color: var(--muted); display: block; margin-bottom: 6px; }
    input, textarea, select { width: 100%; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--border); background: #fff; font-size: 14px; }
    button { padding: 10px 14px; border: 1px solid var(--border); border-radius: 12px; background: #fff; cursor: pointer; }
    button.primary { background: var(--primary); color: #fff; border-color: var(--primary); }
    button.ghost { background: #fff; } .badge { padding: 6px 10px; border-radius: 999px; border: 1px solid var(--border); font-size: 12px; }
    .badge.low { color: var(--success); border-color: var(--success); } .badge.mid { color: var(--warn); border-color: var(--warn); } .badge.high { color: var(--danger); border-color: var(--danger); }
    .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; } .kpi { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
    .progress { height: 10px; background: #E7ECFF; border-radius: 8px; overflow: hidden; } .progress > div { height: 100%; background: var(--primary); }
    .table { width: 100%; border-collapse: collapse; } .table th, .table td { border: 1px solid var(--border); padding: 8px; text-align: left; font-size: 13px; }
    .trend { width: 100%; height: 160px; } footer { color: var(--muted); font-size: 12px; text-align: center; padding: 16px; }
    .hidden { display:none; } @media (max-width: 900px) { .grid, .kpi { grid-template-columns: 1fr; } }
    #errbar { position: fixed; left: 0; right: 0; bottom: 0; background: #ffe8e6; color: #8a1c1c; padding: 8px 12px; font-size: 12px; border-top: 1px solid #f5c2c0; display:none; }
    #build { position: fixed; right: 8px; top: 8px; font-size: 12px; color: #666; }
  </style>
</head>
<body>
  <div id="build">BUILD: offline-v3</div>
  <div id="app"></div>
  <div id="errbar"></div>
  <script>
    (function(){
      var bar = document.getElementById('errbar');
      window.addEventListener('error', function(ev){
        bar.style.display = 'block';
        bar.textContent = '脚本错误：' + ev.message + ' @ ' + (ev.filename||'') + ':' + (ev.lineno||'') + ':' + (ev.colno||'');
      });
    })();

    // 数据
    var LOCAL_DB = {
      users: [{ workerId: '123456', password: '123456', name: '王医生' }],
      patients: {
        '123456': {
          caseId: '123456', name: '张三', assessScore: 78, rehabDay: 12, progressPct: 42, risk: '中级',
          plan: ['上肢主动关节活动 15min × 2','坐立转移训练 10min × 2','步行训练（助行器） 8min × 3','呼吸肌训练 5min × 2'],
          bloodPressure: { sbp: 128, dbp: 82 },
          mobility: { FMA: 58, Berg: 36, '6MWT': 180 },
          caseNotes: [{ id: 'n1', content: '入院第12天，肌力较入院提高一级。', createdAt: '2025-10-25T09:10:00Z' }],
          reports: [{ id: 'r1', title: '头颅CT复查', content: '未见新梗灶，低密度灶略有吸收。', createdAt: '2025-10-24T14:20:00Z' }],
          trends: {
            CBC: [{ date: '2025-10-22', WBC: 6.1, RBC: 4.6, HGB: 135 },{ date: '2025-10-25', WBC: 5.8, RBC: 4.5, HGB: 133 }],
            Coag: [{ date: '2025-10-22', PT: 12.1, INR: 1.0 },{ date: '2025-10-25', PT: 12.4, INR: 1.0 }],
            Lipids: [{ date: '2025-10-22', TC: 4.8, LDL: 2.9, HDL: 1.2, TG: 1.4 },{ date: '2025-10-25', TC: 4.6, LDL: 2.7, HDL: 1.3, TG: 1.3 }],
            Glucose: [{ date: '2025-10-22', FBG: 6.1 },{ date: '2025-10-25', FBG: 5.8 }],
            Inflam: [{ date: '2025-10-22', CRP: 7.2 },{ date: '2025-10-25', CRP: 5.4 }],
            Cardiac: [{ date: '2025-10-22', CK: 95, CKMB: 12, TnI: 0.01 },{ date: '2025-10-25', CK: 88, CKMB: 10, TnI: 0.01 }]
          },
          updatedAt: '2025-10-25T14:45:00Z'
        }
      }
    };
    var KEY = 'doctor_offline_db_v3';
    function loadDB(){ try{ var raw = localStorage.getItem(KEY); if(raw) return JSON.parse(raw);}catch(e){} localStorage.setItem(KEY, JSON.stringify(LOCAL_DB)); return JSON.parse(JSON.stringify(LOCAL_DB)); }
    function saveDB(db){ localStorage.setItem(KEY, JSON.stringify(db)); }
    function resetDB(){ localStorage.removeItem(KEY); location.reload(); }

    var api = {
      login: function(workerId, password){
        var db = loadDB();
        for(var i=0;i<db.users.length;i++){ var u = db.users[i]; if(u.workerId===workerId && u.password===password){ return { ok:true, workerId:workerId, name:u.name }; } }
        return { ok:false, message:'工号或密码错误（默认均为123456）' };
      },
      getPatient: function(caseId){
        var db = loadDB(); var p = db.patients[caseId]; return p ? { ok:true, data:p } : { ok:false, message:'未找到该病例号' };
      },
      addNote: function(caseId, content){
        var db = loadDB(); var p = db.patients[caseId]; if(!p) return { ok:false, message:'未找到该病例号' };
        var item = { id: String(Date.now()), content: content, createdAt: new Date().toISOString() };
        p.caseNotes.unshift(item); saveDB(db); return { ok:true, data:item };
      },
      addReport: function(caseId, title, content){
        var db = loadDB(); var p = db.patients[caseId]; if(!p) return { ok:false, message:'未找到该病例号' };
        var item = { id: String(Date.now()), title: title, content: content, createdAt: new Date().toISOString() };
        p.reports.unshift(item); saveDB(db); return { ok:true, data:item };
      }
    };

    function renderLineChart(selector, series, keys){
      var svg = document.querySelector(selector); if(!svg) return;
      var width = svg.clientWidth || (svg.parentElement && svg.parentElement.clientWidth) || 500;
      var height = svg.clientHeight || 160;
      svg.setAttribute('viewBox', '0 0 ' + width + ' ' + height);
      svg.innerHTML = '';

      if(!series || !series.length){
        var msg = document.createElementNS('http://www.w3.org/2000/svg','text');
        msg.setAttribute('x', 12); msg.setAttribute('y', 24); msg.setAttribute('fill', '#999'); msg.textContent = '无数据'; svg.appendChild(msg); return;
      }
      var pad = { l: 32, r: 8, t: 12, b: 24 };
      var w = width - pad.l - pad.r, h = height - pad.t - pad.b;

      var vals = []; var i, j;
      for (i=0;i<keys.length;i++){ for(j=0;j<series.length;j++){ var v = Number(series[j][keys[i]]); if(!isNaN(v)) vals.push(v); } }
      var minY = Math.min.apply(null, vals), maxY = Math.max.apply(null, vals);
      if(!isFinite(minY) || !isFinite(maxY)){ minY = 0; maxY = 1; }
      var y = function(v){ return pad.t + (h * (1 - (v - minY) / Math.max(1e-6, (maxY - minY)))); };
      var x = function(idx){ var denom = Math.max(1, (series.length - 1)); return pad.l + (w * (idx / denom)); };

      var axis = document.createElementNS('http://www.w3.org/2000/svg','path');
      axis.setAttribute('d', 'M ' + pad.l + ' ' + pad.t + ' V ' + (pad.t + h) + ' H ' + (pad.l + w));
      axis.setAttribute('stroke', '#ddd'); axis.setAttribute('fill', 'none'); svg.appendChild(axis);

      var colors = ['#316CFF','#22A06B','#F5A524','#E5484D','#8B5CF6','#14B8A6'];
      for (i=0;i<keys.length;i++){
        var k = keys[i];
        var path = document.createElementNS('http://www.w3.org/2000/svg','path');
        var d = '';
        for (j=0;j<series.length;j++){ var px = x(j), py = y(Number(series[j][k])); d += (j===0 ? 'M' : 'L') + ' ' + px + ' ' + py + ' '; }
        path.setAttribute('d', d.trim()); path.setAttribute('stroke', colors[i % colors.length]); path.setAttribute('fill', 'none'); path.setAttribute('stroke-width', '2'); svg.appendChild(path);
      }
      for (i=0;i<series.length;i++){ var tx = x(i); var t = document.createElementNS('http://www.w3.org/2000/svg','text'); t.setAttribute('x', tx); t.setAttribute('y', height-6); t.setAttribute('font-size', '11'); t.setAttribute('text-anchor','middle'); t.setAttribute('fill', '#666'); t.textContent = (series[i].date||'').slice(5); svg.appendChild(t); }
    }

    function $(sel){ return document.querySelector(sel); }
    var app = null;
    var state = { workerId:null, doctorName:null, patient:null };

    function badge(level){ var map = { '低级':'low', '中级':'mid', '高级':'high' }; var cls = map[level] || 'mid'; return '<span class=\"badge ' + cls + '\">' + level + '</span>'; }
    function esc(s){ return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }

    function LoginView(){
      app.innerHTML = ''
        + '<div class=\"container\">'
        + '  <div class=\"card\" style=\"max-width:420px;margin:10vh auto;\">'
        + '    <h1>医生端登录</h1>'
        + '    <div class=\"space\"></div>'
        + '    <label>工号</label>'
        + '    <input id=\"workerId\" value=\"123456\" placeholder=\"请输入工号\"/>'
        + '    <div class=\"space\"></div>'
        + '    <label>密码</label>'
        + '    <input id=\"password\" value=\"123456\" type=\"password\" placeholder=\"请输入密码\"/>'
        + '    <div class=\"space\"></div>'
        + '    <button id=\"loginBtn\" class=\"primary\" style=\"width:100%;\">登录</button>'
        + '    <div class=\"space\"></div>'
        + '    <div id=\"loginErr\" style=\"color:#E5484D;font-size:12px;\"></div>'
        + '    <div class=\"space\"></div>'
        + '    <button id=\"resetDb\" class=\"ghost\" style=\"width:100%;\">重置演示数据</button>'
        + '  </div>'
        + '</div>';
      $('#loginBtn').onclick = function(){
        var workerId = $('#workerId').value.trim();
        var password = $('#password').value.trim();
        var resp = api.login(workerId, password);
        if(!resp.ok){ $('#loginErr').textContent = resp.message || '登录失败'; return; }
        state.workerId = resp.workerId; state.doctorName = resp.name; DashboardView();
      };
      $('#resetDb').onclick = resetDB;
    }

    function DashboardView(){
      app.innerHTML = ''
        + '<div class=\"container\">'
        + '  <div class=\"row\" style=\"justify-content:space-between;\">'
        + '    <h1>医生工作台</h1>'
        + '    <div>工号：<strong>' + (state.workerId||'') + '</strong>（' + (state.doctorName||'—') + '）'
        + '      <button id=\"logout\" class=\"ghost\" style=\"margin-left:8px;\">退出</button>'
        + '    </div>'
        + '  </div>'
        + '  <div class=\"card\">'
        + '    <div class=\"row\">'
        + '      <div style=\"flex:1;\">'
        + '        <label>输入患者病例号</label>'
        + '        <input id=\"caseIdInput\" value=\"123456\" placeholder=\"如：123456\"/>'
        + '      </div>'
        + '      <div><button id=\"searchBtn\" class=\"primary\">查询</button></div>'
        + '    </div>'
        + '    <div id=\"searchErr\" style=\"color:#E5484D;font-size:12px;margin-top:8px;\"></div>'
        + '  </div>'
        + '  <div id=\"patientArea\" class=\"' + (state.patient ? '' : 'hidden') + '\">'
        + '    <div class=\"space\"></div>'
        + '    <div class=\"grid\">'
        + '      <div class=\"card\"><h2>患者概览</h2><div id=\"summary\"></div></div>'
        + '      <div class=\"card\"><h2>训练计划</h2><ul id=\"planList\"></ul></div>'
        + '    </div>'
        + '    <div class=\"space\"></div>'
        + '    <div class=\"kpi\">'
        + '      <div class=\"card\"><label>评估指数</label><div id=\"kpiScore\" style=\"font-size:28px;font-weight:700;\">--</div></div>'
        + '      <div class=\"card\"><label>康复阶段</label><div id=\"kpiDay\" style=\"font-size:28px;font-weight:700;\">--</div></div>'
        + '      <div class=\"card\"><label>进度</label><div><div class=\"progress\"><div id=\"kpiProg\" style=\"width:0%;\"></div></div><div id=\"kpiProgText\" style=\"margin-top:6px;\">--%</div></div></div>'
        + '      <div class=\"card\"><label>风险等级</label><div id=\"kpiRisk\" style=\"margin-top:6px;\"></div></div>'
        + '    </div>'
        + '    <div class=\"space\"></div>'
        + '    <div class=\"grid\">'
        + '      <div class=\"card\">'
        + '        <h2>写入病例</h2>'
        + '        <textarea id=\"noteContent\" rows=\"5\" placeholder=\"填写病程记录、查房记录等...\"></textarea>'
        + '        <div class=\"space\"></div><button id=\"addNote\" class=\"primary\">保存记录</button><div class=\"space\"></div>'
        + '        <table class=\"table\" id=\"notesTable\"><thead><tr><th>时间</th><th>内容</th></tr></thead><tbody></tbody></table>'
        + '      </div>'
        + '      <div class=\"card\">'
        + '        <h2>检测报告</h2>'
        + '        <input id=\"reportTitle\" placeholder=\"报告标题，如：MRI复查\"/>'
        + '        <div class=\"space\"></div><textarea id=\"reportContent\" rows=\"5\" placeholder=\"报告内容...\"></textarea>'
        + '        <div class=\"space\"></div><button id=\"addReport\" class=\"primary\">保存报告</button><div class=\"space\"></div>'
        + '        <table class=\"table\" id=\"reportsTable\"><thead><tr><th>时间</th><th>标题</th><th>内容</th></tr></thead><tbody></tbody></table>'
        + '      </div>'
        + '    </div>'
        + '    <div class=\"space\"></div>'
        + '    <div class=\"card\">'
        + '      <h2>患者指标趋势</h2>'
        + '      <div class=\"grid\">'
        + '        <div><label>血常规 (WBC / RBC / HGB)</label><svg id=\"chartCBC\" class=\"trend\"></svg></div>'
        + '        <div><label>凝血功能 (PT / INR)</label><svg id=\"chartCoag\" class=\"trend\"></svg></div>'
        + '        <div><label>血脂 (TC / LDL / HDL / TG)</label><svg id=\"chartLipids\" class=\"trend\"></svg></div>'
        + '        <div><label>血糖 (FBG)</label><svg id=\"chartGlucose\" class=\"trend\"></svg></div>'
        + '        <div><label>炎症标志物 (CRP)</label><svg id=\"chartInflam\" class=\"trend\"></svg></div>'
        + '        <div><label>心肌酶 (CK / CKMB / TnI)</label><svg id=\"chartCardiac\" class=\"trend\"></svg></div>'
        + '      </div>'
        + '    </div>'
        + '    <div class=\"space\"></div>'
        + '    <div class=\"card\">'
        + '      <h2>患者行动能力评估</h2>'
        + '      <table class=\"table\" id=\"mobilityTable\"><thead><tr><th>量表</th><th>数值</th></tr></thead><tbody></tbody></table>'
        + '    </div>'
        + '  </div>'
        + '  <div class=\"space\"></div>'
        + '  <footer>医生端 · 脑梗康复管理（离线演示 v3） | <a href=\"#\" id=\"resetLink\">重置数据</a></footer>'
        + '</div>';
      document.querySelector('#logout').onclick = function(){ state.workerId=null; state.patient=null; LoginView(); };
      document.querySelector('#resetLink').onclick = function(e){ e.preventDefault(); resetDB(); };
      document.querySelector('#searchBtn').onclick = function(){
        var caseId = document.querySelector('#caseIdInput').value.trim();
        var resp = api.getPatient(caseId);
        if(!resp.ok){ document.querySelector('#searchErr').textContent = resp.message || '查询失败'; document.querySelector('#patientArea').classList.add('hidden'); return; }
        document.querySelector('#searchErr').textContent=''; state.patient = resp.data; renderPatient();
      };
    }

    function renderPatient(){
      if(!state.patient) return;
      document.querySelector('#patientArea').classList.remove('hidden');
      var p = state.patient;
      var sbp = (p.bloodPressure && p.bloodPressure.sbp) ? p.bloodPressure.sbp : '-';
      var dbp = (p.bloodPressure && p.bloodPressure.dbp) ? p.bloodPressure.dbp : '-';

      document.querySelector('#summary').innerHTML = ''
        + '<div class=\"row\" style=\"justify-content:space-between;\">'
        + '  <div><div>病例号：<strong>' + (p.caseId||'') + '</strong></div>'
        + '  <div style=\"color:#666;margin-top:6px;\">姓名：' + (p.name||'—') + '</div>'
        + '  <div style=\"color:#666;margin-top:6px;\">血压：' + sbp + '/' + dbp + ' mmHg</div></div>'
        + '  <div>' + badge(p.risk) + '</div>'
        + '</div>';

      var i, html = '';
      for(i=0;i<(p.plan||[]).length;i++){ html += '<li>' + p.plan[i] + '</li>'; }
      document.querySelector('#planList').innerHTML = html;

      document.querySelector('#kpiScore').textContent = (p.assessScore||0) + '/100';
      document.querySelector('#kpiDay').textContent = '第 ' + (p.rehabDay||0) + ' 天';
      document.querySelector('#kpiProg').style.width = (p.progressPct||0) + '%';
      document.querySelector('#kpiProgText').textContent = (p.progressPct||0) + '%';
      document.querySelector('#kpiRisk').innerHTML = badge(p.risk||'中级');

      var ntb = document.querySelector('#notesTable tbody'); html='';
      var notes = p.caseNotes || [];
      for(i=0;i<notes.length;i++){ var n = notes[i]; html += '<tr><td>' + new Date(n.createdAt).toLocaleString() + '</td><td>' + esc(n.content||'') + '</td></tr>'; }
      ntb.innerHTML = html;

      var rtb = document.querySelector('#reportsTable tbody'); html='';
      var reps = p.reports || [];
      for(i=0;i<reps.length;i++){ var r = reps[i]; html += '<tr><td>' + new Date(r.createdAt).toLocaleString() + '</td><td>' + esc(r.title||'') + '</td><td>' + esc(r.content||'') + '</td></tr>'; }
      rtb.innerHTML = html;

      var mtb = document.querySelector('#mobilityTable tbody'); html='';
      var mob = p.mobility || {}; for(var k in mob){ if(Object.prototype.hasOwnProperty.call(mob,k)){ html += '<tr><td>' + k + '</td><td>' + mob[k] + '</td></tr>'; } }
      mtb.innerHTML = html;

      var t = p.trends || {};
      renderLineChart('#chartCBC', t.CBC || [], ['WBC','RBC','HGB']);
      renderLineChart('#chartCoag', t.Coag || [], ['PT','INR']);
      renderLineChart('#chartLipids', t.Lipids || [], ['TC','LDL','HDL','TG']);
      renderLineChart('#chartGlucose', t.Glucose || [], ['FBG']);
      renderLineChart('#chartInflam', t.Inflam || [], ['CRP']);
      renderLineChart('#chartCardiac', t.Cardiac || [], ['CK','CKMB','TnI']);

      document.querySelector('#addNote').onclick = function(){
        var content = document.querySelector('#noteContent').value.trim(); if(!content) return;
        var resp = api.addNote(p.caseId, content); if(resp.ok){ document.querySelector('#noteContent').value=''; state.patient.caseNotes.unshift(resp.data); renderPatient(); }
      };
      document.querySelector('#addReport').onclick = function(){
        var title = document.querySelector('#reportTitle').value.trim() || '未命名报告';
        var content = document.querySelector('#reportContent').value.trim(); if(!content) return;
        var resp = api.addReport(p.caseId, title, content); if(resp.ok){ document.querySelector('#reportTitle').value=''; document.querySelector('#reportContent').value=''; state.patient.reports.unshift(resp.data); renderPatient(); }
      };
    }

    try { var appEl = document.getElementById('app'); if(appEl){ window.app = appEl; app = appEl; LoginView(); } } catch (e) {
      var bar = document.getElementById('errbar'); bar.style.display='block'; bar.textContent = '初始化失败：' + (e && e.message ? e.message : e);
    }
  </script>
</body>
</html>
